package org.feng.handler;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.HexValue;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import org.feng.annotions.DataPermission;
import org.feng.common.RequestHeaderUserInfo;
import org.feng.common.RequestHolder;
import org.feng.common.constant.PermissionDataScope;
import org.feng.common.util.SpringContextUtil;
import org.feng.interceptor.MyDataPermissionHandler;
import org.feng.service.IRolePermissionService;
import org.feng.service.IRoleUserService;
import org.feng.service.IUserService;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 通过角色做数据权限处理
 *
 * @author fengjinsong
 */
@Slf4j
public class RoleDataPermissionHandler implements MyDataPermissionHandler {

    private static IRoleUserService roleUserService;

    private static IRolePermissionService rolePermissionService;

    private static IUserService userService;

    public RoleDataPermissionHandler() {
        roleUserService = SpringContextUtil.getBean(IRoleUserService.class);
        rolePermissionService = SpringContextUtil.getBean(IRolePermissionService.class);
        userService = SpringContextUtil.getBean(IUserService.class);
    }

    @SneakyThrows(Exception.class)
    @Override
    public Expression getSqlSegmentWithPermission(PlainSelect plainSelect, String whereSegment) {

        // 待执行 SQL Where 条件表达式
        Expression where = plainSelect.getWhere();
        if (where == null) {
            where = new HexValue(" 1 = 1 ");
        }
        log.debug("开始进行权限过滤,where: {},mappedStatementId: {}", where, whereSegment);
        //获取mapper名称
        String className = whereSegment.substring(0, whereSegment.lastIndexOf("."));
        //获取方法名
        String methodName = whereSegment.substring(whereSegment.lastIndexOf(".") + 1);
        Table fromItem = (Table) plainSelect.getFromItem();
        // 有别名用别名，无别名用表名，防止字段冲突报错
        Alias fromItemAlias = fromItem.getAlias();
        String mainTableName = fromItemAlias == null ? fromItem.getName() : fromItemAlias.getName();
        //获取当前mapper 的方法
        Method[] methods = Class.forName(className).getMethods();
        //遍历判断mapper 的所以方法，判断方法上是否有 UserDataPermission
        for (Method m : methods) {
            if (Objects.equals(m.getName(), methodName)) {
                boolean annotationPresent = m.isAnnotationPresent(DataPermission.class);
                if (!annotationPresent) {
                    log.info("当前方法无权限注解，跳过权限处理");
                    return where;
                }

                try {
                    log.info("权限处理开始");
                    DataPermission annotation = m.getAnnotation(DataPermission.class);
                    // 当前用户信息
                    RequestHeaderUserInfo userInfo = RequestHolder.REQUEST_HEADER_USER_INFO_THREAD_LOCAL.get();
                    // 当前用户角色
                    Set<Integer> roleIdSet = roleUserService.getRoleIdSet(userInfo.getUserId());
                    // 如果启用了请求中的角色，即当前角色
                    if (annotation.useRequestRole()) {
                        roleIdSet = Collections.singleton(userInfo.getRoleId());
                    }
                    // 权限编码
                    Set<String> permissionCode = rolePermissionService.listPermissionCodeByRole(roleIdSet);

                    // 拥有查询全部数据权限
                    if (permissionCode.contains(PermissionDataScope.ALL.getCode())) {
                        return GET_EXPRESSION_BY_PERMISSION_CODE_CACHE_MAP.get(PermissionDataScope.ALL)
                                .get(where, mainTableName, annotation, userInfo);
                    }

                    // 查看本部门数据
                    if (permissionCode.contains(PermissionDataScope.DEPT.getCode())) {
                        return GET_EXPRESSION_BY_PERMISSION_CODE_CACHE_MAP.get(PermissionDataScope.DEPT)
                                .get(where, mainTableName, annotation, userInfo);
                    }

                    // 只能查看自己
                    if (permissionCode.contains(PermissionDataScope.MYSELF.getCode())) {
                        return GET_EXPRESSION_BY_PERMISSION_CODE_CACHE_MAP.get(PermissionDataScope.MYSELF)
                                .get(where, mainTableName, annotation, userInfo);
                    }
                } finally {
                    log.info("权限处理结束");
                }
            }
        }
        //说明无权查看，
        where = new HexValue(" 1 = 2 ");
        return where;
    }


    @FunctionalInterface
    private interface GetExpressionByPermissionCode {
        Expression get(Expression where, String mainTableName, DataPermission dataPermission, RequestHeaderUserInfo userInfo);
    }

    private static final Map<PermissionDataScope, GetExpressionByPermissionCode> GET_EXPRESSION_BY_PERMISSION_CODE_CACHE_MAP = new LinkedHashMap<>();

    static {
        GET_EXPRESSION_BY_PERMISSION_CODE_CACHE_MAP.put(PermissionDataScope.ALL, (where, mainTableName, dataPermission, userInfo) -> {
            log.info("拥有查询全部数据权限，查询全部");
            return where;
        });

        GET_EXPRESSION_BY_PERMISSION_CODE_CACHE_MAP.put(PermissionDataScope.DEPT, (where, mainTableName, dataPermission, userInfo) -> {
            log.info("拥有查看本部门数据权限，查询部门数据");
            // 查看本部门用户数据
            List<Integer> deptUserList = userService.listUserId(userInfo.getUserId());
            // 把集合转变为JSQLParser需要的元素列表
            ItemsList deptList = new ExpressionList(deptUserList.stream().map(LongValue::new).collect(Collectors.toList()));
            InExpression inExpressiondept = new InExpression(new Column(mainTableName + "." + dataPermission.permissionFieldName()), deptList);
            return new AndExpression(where, inExpressiondept);
        });

        GET_EXPRESSION_BY_PERMISSION_CODE_CACHE_MAP.put(PermissionDataScope.MYSELF, (where, mainTableName, dataPermission, userInfo) -> {
            log.info("拥有查询自己数据的权限，只能查询自己的数据");
            EqualsTo usesEqualsTo = new EqualsTo();
            usesEqualsTo.setLeftExpression(new Column(mainTableName + "." + dataPermission.permissionFieldName()));
            usesEqualsTo.setRightExpression(new LongValue(String.valueOf(userInfo.getUserId())));
            return new AndExpression(where, usesEqualsTo);
        });
    }
}
